맨위로가기

동적 배열

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

동적 배열은 배열과 유사하게 요소에 순차적으로 접근할 수 있으며, 필요에 따라 크기를 동적으로 조절할 수 있는 자료 구조이다. 배열의 장점을 유지하면서도, 요소의 추가 및 삭제 연산을 효율적으로 처리할 수 있도록 설계되었다. 동적 배열은 크기 조정에 따른 성능 저하를 상쇄하기 위해, 일반적으로 공간을 미리 확보하는 방식으로 구현되며, 다양한 프로그래밍 언어에서 지원된다.

더 읽어볼만한 페이지

  • 분할 상환 자료 구조 - 스플레이 트리
    스플레이 트리는 스플레이 연산을 통해 자가 균형을 유지하며 최근 접근 노드의 빠른 접근을 가능하게 하는 이진 탐색 트리로, 분할 상환 분석 시 평균적으로 O(log n)의 시간 복잡도를 가진다.
  • 분할 상환 자료 구조 - 피보나치 힙
    피보나치 힙은 최소 힙 속성을 가진 트리들의 집합으로, 각 노드의 차수를 특정 로그 값 이하로 유지하여 효율적인 삽입, 병합, 최소값 검색 연산을 지원하며, 다익스트라 알고리즘과 같은 그래프 알고리즘의 성능 향상에 활용된다.
  • 배열 - 원형 버퍼
    원형 버퍼는 고정 크기의 자료구조로, 처음과 끝이 연결되어 데이터가 순환하며 저장되는 FIFO 방식의 버퍼이며, 큐 구현, 멀티미디어 스트리밍, 데이터 압축 등에 활용된다.
  • 배열 - 할바흐 배열
    할바흐 배열은 특정 면에 자기장을 집중시키고 다른 면에서는 상쇄시키는 배열로, 자장 격리에 유용하며 다양한 산업 및 첨단 기술 분야에 응용된다.
동적 배열
개요
자료 구조 유형배열
접근Θ(1)
탐색Θ(n)
삽입Θ(n)
삭제Θ(n)
공간 복잡도Θ(n)
상세 정보
별칭동적 테이블
확장 가능한 배열
동적 배열
사용 사례해시 테이블

2. 작동 원리 및 특징

동적 배열은 그 크기가 고정되지 않고, 필요에 따라 자동으로 늘어나거나 줄어드는 배열이다. 이러한 특징은 데이터의 개수를 미리 예측하기 어렵거나, 데이터가 계속 추가/삭제되는 상황에서 유용하다.

동적 배열은 내부적으로 고정 크기 배열을 사용한다. 이 고정 크기 배열에 데이터를 순차적으로 저장하며, 남는 공간은 예비 공간으로 남겨둔다. 만약 데이터를 추가하다가 예비 공간이 부족해지면, 더 큰 크기의 새로운 배열을 할당하고 기존 데이터를 복사하는 방식으로 작동한다.[2] 이 과정을 '크기 조정'이라고 하며, 일반적으로 현재 크기의 두 배로 늘리는 방식을 사용한다.

동적 배열의 끝에 요소를 추가하는 작업은 예비 공간이 충분하다면 상수 시간에 가능하다. 하지만 크기 조정이 필요한 경우에는 모든 요소를 복사해야 하므로 시간이 더 오래 걸린다. 이러한 크기 조정 비용에도 불구하고, 여러 번의 추가 작업을 고려하면 평균적으로 상수 시간에 가까운 성능을 보인다. 이를 상각 상수 시간이라고 한다.[5][6]

동적 배열은 배열과 마찬가지로 특정 위치의 요소를 빠르게 읽거나 변경할 수 있다. 또한, 요소들이 메모리에 연속적으로 저장되기 때문에 캐시 효율성이 높다는 장점도 있다. 하지만 중간에 요소를 삽입하거나 삭제하는 경우에는 뒤쪽의 요소들을 모두 이동시켜야 하므로 시간이 오래 걸린다.

다음은 동적 배열과 다른 자료 구조들의 성능을 비교한 표이다.

리스트 자료 구조 비교
rowspan=2 |조회
(인덱스)
변경 (삽입 또는 삭제)초과 공간,
평균
시작중간
연결 리스트Θ(n)Θ(1)끝 요소가 알려진 경우 Θ(1),
끝 요소가 알려지지 않은 경우 Θ(n)
Θ(n)
배열Θ(1)0
동적 배열Θ(1)Θ(n)Θ(1) 상각Θ(n)[14]
균형 트리Θ(log n)Θ(log n)Θ(log n)Θ(n)
임의 접근 리스트Θ(log n)[15]Θ(1)Θ(n)
해시된 배열 트리Θ(1)Θ(n)Θ(1) 상각Θ(√n)


2. 1. 용량과 크기 조정

간단한 동적 배열은 고정 크기 배열을 할당하여 구성할 수 있으며, 일반적으로 필요한 요소 수보다 크게 할당한다. 동적 배열의 요소는 기본 배열의 시작 부분에 연속적으로 저장되며, 나머지 위치는 예약되거나 사용되지 않는다. 동적 배열의 끝에 요소를 추가하는 것은 예약된 공간을 사용하여 상수 시간에 가능하다. 모든 공간이 소모된 경우, 기본 고정 크기 배열의 크기를 늘려야 한다. 일반적으로 크기 조정은 새 배열을 할당하고 원래 배열에서 각 요소를 복사해야 하므로 비용이 많이 든다. 크기 조정이 필요하지 않으므로 동적 배열의 끝에서 요소를 상수 시간에 제거할 수 있다.[2]

동적 배열 내용에서 사용되는 요소의 수는 ''논리적 크기'' 또는 ''크기''이며, 기본 배열의 크기는 동적 배열의 ''용량'' 또는 ''물리적 크기''라고 하며, 이는 데이터를 재배치하지 않고 가능한 최대 크기이다.[2]

동적 배열은 크기를 두 배로 늘리는 등 큰 폭으로 크기를 조정하며, 미래 확장을 위해 예약된 공간을 사용한다. ''n''개의 요소가 삽입되면, 용량은 기하 수열을 형성한다. 배열을 임의의 상수 비율 ''a''로 확장하면 ''n''개의 요소를 삽입하는 데 전체적으로 ''O''(''n'') 시간이 걸리도록 보장하며, 이는 각 삽입이 상각 상수 시간이 걸린다는 의미이다. 많은 동적 배열은 크기가 용량의 특정 임계값, 예를 들어 30% 미만으로 떨어지면 기본 스토리지를 일부 할당 해제한다.

동적 배열의 성장 인자는 공간-시간 트레이드 오프 및 메모리 할당기 자체에 사용되는 알고리즘을 포함한 여러 요인에 따라 달라진다. 성장 인자 ''a''에 대해 삽입 연산당 평균 시간은 약 ''a''/(''a''−1)이며, 낭비되는 셀 수는 (''a''−1)''n''로 상한을 둔다. 황금비율과 1.5 값을 포함하여 이상적인 성장 인자 값에 대한 다양한 논의가 있어왔다.[4] 그러나 많은 교과서에서는 단순성과 분석 목적으로 ''a'' = 2를 사용한다.[5][6]

다음은 몇 가지 인기 있는 구현에서 사용되는 성장 인자이다.

구현성장 인자 (a)
자바 ArrayList[1]1.5 (3/2)
파이썬 PyListObject[7]~1.125 (n + (n >> 3))
마이크로소프트 비주얼 C++ 2013[8]1.5 (3/2)
G++ 5.2.0[3]2
Clang 3.6[3]2
페이스북 folly/FBVector[9]1.5 (3/2)
러스트 Vec[10]2
Go 슬라이스[11]1.25와 2 사이
시퀀스[12]2
SBCL (커먼 리스프) 벡터[13]2
C# (.NET) 8) List2


2. 2. 성장 인자 (Growth Factor)

동적 배열은 크기를 조절할 때, 일반적으로 현재 크기의 두 배로 늘리는 방식을 사용한다. 이는 잦은 크기 조정으로 인한 비용을 줄이고, 미래에 추가될 요소를 위한 공간을 확보하기 위함이다.[5][6]

예를 들어, ''n''개의 요소를 동적 배열의 끝에 추가하면, 배열의 용량은 기하 수열 형태로 증가한다. 배열의 크기를 상수 비율 ''a''로 늘리면, ''n''개의 요소를 추가하는 데 걸리는 총 시간은 ''O''(''n'')이 된다. 즉, 각 요소 추가 작업은 상각 상수 시간이 걸린다.

동적 배열의 성장 인자는 공간-시간 트레이드 오프 등 여러 요인에 따라 결정된다. 성장 인자 ''a''에 대해 삽입 연산당 평균 시간은 약 ''a'' / (''a'' - 1)이며, 낭비되는 셀 수는 (''a'' - 1)''n''을 상한으로 한다. 메모리 할당 알고리즘에 따라서는 성장 인자 값 ''a'' = 2와 같은 값은 상당한 양의 메모리가 여전히 사용 가능하더라도 동적 배열 확장이 메모리 부족으로 실행될 수 있다.[3] 황금비율과 1.5 값을 포함하여 이상적인 성장 인자 값에 대한 다양한 논의가 있어왔다.[4] 그러나 많은 교과서에서는 단순성과 분석 목적으로 ''a'' = 2를 사용한다.[5][6]

몇 가지 프로그래밍 언어 및 구현체 별 성장 인자는 다음과 같다.

구현성장 인자 (a)
자바 ArrayList[1]1.5 (3/2)
파이썬 PyListObject[7]~1.125 (n + (n >> 3))
마이크로소프트 비주얼 C++ 2013[8]1.5 (3/2)
G++ 5.2.0[3]2
Clang 3.6[3]2
페이스북 folly/FBVector[9]1.5 (3/2)
러스트 Vec[10]2
Go 슬라이스[11]1.25와 2 사이
시퀀스[12]2
SBCL (커먼 리스프) 벡터[13]2
C# (.NET) 8) List2


2. 3. 성능 분석

(인덱스)변경 (삽입 또는 삭제)초과 공간,
평균시작끝중간연결 리스트Θ(n)Θ(1)끝 요소가 알려진 경우 Θ(1),
끝 요소가 알려지지 않은 경우 Θ(n)Θ(n)배열Θ(1)0동적 배열Θ(1)Θ(n)Θ(1) 상각Θ(n)[14]균형 트리Θ(log n)Θ(log n)Θ(log n)Θ(n)임의 접근 리스트Θ(log n)[15]Θ(1)[15]Θ(n)해시된 배열 트리Θ(1)Θ(n)Θ(1) 상각Θ(√n)


3. 변형

갭 버퍼는 동적 배열과 유사하지만 동일한 임의 위치 근처에서 삽입 및 삭제 작업을 효율적으로 수행할 수 있게 해준다. 일부 데크 구현은 배열 데크를 사용하는데, 이는 한쪽 끝뿐만 아니라 양쪽 끝에서도 분할 상환 상수 시간으로 삽입/제거를 할 수 있게 해준다.

Goodrich[16]는 배열의 어느 곳에서나 삽입 및 삭제에 대해 ''O''(''n''1/''k'') 성능을 제공하고, ''O''(''k'')의 접근 및 설정 시간을 가지는 '계층적 벡터'(''tiered vectors'')라는 동적 배열 알고리즘을 제시했다. 여기서 ''k'' ≥ 2는 상수 매개변수이다.

1999년 논문[18]에서 Brodnik et al.은 매 시점 ''n''개 요소에 대해 ''n''1/2 공간만 낭비하는 계층화된 동적 배열 데이터 구조를 설명하고, 모든 동적 배열이 분할 상환 상수 시간을 유지하려면 이만큼의 공간을 낭비해야 한다는 하한을 증명했다. 또한 버퍼를 확장 및 축소하는 것이 분할 상환뿐만 아니라 최악의 경우에도 상수 시간이 걸리는 변형을 제시했다.

단순하게 크기를 조절할 수 있는 배열은 배열에 추가되는 모든 항목에 대해 realloc을 호출하여 배열에 포함된 모든 데이터에 정확히 충분한 할당된 크기를 유지한다. 이는 C에서 크기를 조절할 수 있는 배열을 구현하는 가장 간단한 방법이지만, 메모리를 낭비하지 않는 대신 배열의 끝에 추가하는 데 항상 Θ(''n'') 시간이 걸린다.[17][20][21][22][23]

선형으로 증가하는 배열은 배열의 크기를 조정할 때마다 Θ(1) 공간을 미리 할당("낭비")하므로 단순하게 크기를 조절할 수 있는 배열보다 훨씬 빠르다. 배열의 끝에 추가하는 데 여전히 Θ(''n'') 시간이 걸리지만, 상수가 훨씬 작다.

단순하게 크기를 조절할 수 있는 배열과 선형으로 증가하는 배열은 공간이 제한된 응용 프로그램에서 많은 소규모의 크기를 조절할 수 있는 배열이 필요할 때 유용할 수 있으며, 지수적으로 증가하는 동적 배열로 이어지는 교육적인 예제로도 흔히 사용된다.[24]

3. 1. 갭 버퍼 (Gap Buffer)

갭 버퍼는 동적 배열과 유사하지만 동일한 임의 위치 근처에서 클러스터링된 삽입 및 삭제 작업을 효율적으로 수행할 수 있다. 일부 데크 구현은 배열 데크를 사용하는데, 이는 한쪽 끝뿐만 아니라 양쪽 끝에서도 분할 상환 상수 시간으로 삽입/제거를 할 수 있다.[16]

3. 2. 계층적 벡터 (Tiered Vectors)

Goodrich[16]는 배열의 어느 곳에서나 삽입 및 삭제에 대해 ''O''(''n''1/''k'') 성능을 제공하고 ''O''(''k'')를 얻고 설정하는 '계층적 벡터'(''tiered vectors'')라는 동적 배열 알고리즘을 제시했으며, 여기서 ''k'' ≥ 2는 상수 매개변수이다.

해시 배열 트리(HAT)는 1996년 Sitarski에 의해 발표된 동적 배열 알고리즘이다.[17] 해시 배열 트리는 배열의 요소 수가 ''n''일 때 ''n''1/2 차수의 저장 공간을 낭비한다. 이 알고리즘은 해시 배열 트리의 끝에 일련의 객체를 추가할 때 ''O''(1) 분할 상환 성능을 갖는다.

1999년 논문[18]에서 Brodnik et al.은 매 시점 ''n''개 요소에 대해 ''n''1/2 공간만 낭비하는 계층화된 동적 배열 데이터 구조를 설명하고, 모든 동적 배열이 분할 상환 상수 시간을 유지하려면 이만큼의 공간을 낭비해야 한다는 하한을 증명했다. 또한 버퍼를 확장 및 축소하는 것이 분할 상환뿐만 아니라 최악의 경우에도 상수 시간이 걸리는 변형을 제시했다.

Bagwell (2002)[19]은 동적 배열을 구현하는 데 적용할 수 있는 VList 알고리즘을 제시했다.

3. 3. 해시 배열 트리 (Hashed Array Tree, HAT)

해시 배열 트리(HAT)는 1996년 시타르스키(Sitarski)가 발표한 동적 배열 알고리즘이다.[17] 해시 배열 트리는 배열의 요소 수가 ''n''일 때 ''n''1/2 크기의 저장 공간을 낭비한다. 이 알고리즘은 해시 배열 트리의 끝에 일련의 객체를 추가할 때 ''O''(1) 분할 상환 성능을 갖는다.

3. 4. VList

배그웰(Bagwell, 2002)은 동적 배열을 구현하는 데 사용할 수 있는 VList 알고리즘을 제시했다.[19]

4. 언어 지원

C++의 `std::vector`와 러스트의 `std::vec::Vec`은 동적 배열의 구현이며, 자바 API[26]와 .NET Framework에서 제공되는 `ArrayList`[25] 클래스도 동적 배열의 구현이다.[27][28]

.NET Framework 버전 2.0에서 제공되는 제네릭 `List<>` 클래스 또한 동적 배열로 구현된다. 스몰토크의 `OrderedCollection`은 동적 시작 및 종료 인덱스를 가진 동적 배열로, 첫 번째 요소 제거도 O(1)이다.

파이썬의 `list` 데이터 타입 구현은 동적 배열이며, 증가 패턴은 0, 4, 8, 16, 24, 32, 40, 52, 64, 76, ...[29]이다.

델파이와 D는 언어의 핵심에서 동적 배열을 구현한다.

Ada의 `Ada.Containers.Vectors` 제네릭 패키지는 주어진 서브타입에 대한 동적 배열 구현을 제공한다.

펄과 루비와 같은 많은 스크립트 언어는 동적 배열을 내장 원시 자료형으로 제공한다.

C를 위한 여러 크로스 플랫폼 프레임워크는 Core Foundation의 `CFArray`와 `CFMutableArray`, GLib의 `GArray`와 `GPtrArray`를 포함하여 동적 배열 구현을 제공한다.

Common Lisp는 내장된 `array` 타입을 ''조절 가능(adjustable)''으로 구성하고 삽입 위치를 ''채우기 포인터(fill-pointer)''로 구성하여 크기 조절 가능한 벡터에 대한 기본적인 지원을 제공한다.

참조

[1] 문서 source code of java.util.ArrayList class from OpenJDK 6 http://hg.openjdk.ja[...]
[2] 서적 Physical size and logical size https://books.google[...] Cengage Learning
[3] 웹사이트 C++ STL vector: definition, growth factor, member functions https://web.archive.[...] 2015-08-05
[4] 웹사이트 vector growth factor of 1.5 http://arquivo.pt/wa[...] Google Groups 2015-08-05
[5] 서적 Algorithm Design: Foundations, Analysis and Internet Examples Wiley
[6] 서적 Introduction to Algorithms
[7] Github List object implementation https://github.com/p[...] 2020-03-23
[8] 웹사이트 Dissecting the C++ STL Vector: Part 3 - Capacity & Size https://hadibrais.wo[...] 2015-08-05
[9] 웹사이트 facebook/folly https://github.com/f[...] 2015-08-05
[10] 웹사이트 rust-lang/rust https://github.com/r[...] 2020-06-09
[11] 웹사이트 golang/go https://github.com/g[...] 2021-09-14
[12] 웹사이트 The Nim memory model http://zevv.nl/nim-m[...] 2022-05-24
[13] 웹사이트 sbcl/sbcl https://github.com/s[...] 2023-02-15
[14] 간행물 Resizable Arrays in Optimal Time and Space (Technical Report CS-99-09) http://www.cs.uwater[...] Department of Computer Science, University of Waterloo
[15] 논문 Purely Functional Random-Access Lists
[16] 간행물 Tiered Vectors: Efficient Dynamic Arrays for Rank-Based Sequences https://archive.org/[...]
[17] 간행물 HATs: Hashed array trees http://www.ddj.com/a[...] 1996-09
[18] Technical Report CS-99-09 Resizable Arrays in Optimal Time and Space http://www.cs.uwater[...] Department of Computer Science, University of Waterloo
[19] 간행물 Fast Functional Lists, Hash-Lists, Deques and Variable Length Arrays http://citeseer.ist.[...] EPFL
[20] 문서 Dynamic Arrays https://w3.cs.jmu.ed[...]
[21] 문서 Amortized Time https://users.cs.nor[...]
[22] 문서 Hashed Array Tree: Efficient representation of Array https://iq.opengenus[...]
[23] 문서 Different notions of complexity https://people.ksp.s[...]
[24] 문서 Dynamic arrays in C https://www.strchr.c[...]
[25] Javadoc java.util.ArrayList
[26] 서적 "Effective Java: Programming Language Guide" Addison-Wesley
[27] 문서 ArrayList Class https://msdn.microso[...]
[28] 서적 C# in Depth Manning
[29] Github listobject.c (github.com) https://github.com/p[...]
[30] 문서 source code of java.util.ArrayList class from OpenJDK 6 http://hg.openjdk.ja[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com